
package ogame.utilities.datastorage;

import com.almworks.sqlite4java.SQLiteConnection;
import com.almworks.sqlite4java.SQLiteException;
import java.io.File;
import ogame.utilities.Constants;
import ogame.utilities.Coords;
import ogame.utilities.Player;
import ogame.utilities.Resources;
import ogame.utilities.buildings.BuildingType;
import ogame.utilities.buildings.Buildings;
import ogame.utilities.datastorage.subtables.GeneralTable;
import ogame.utilities.datastorage.subtables.PlanetTable;
import ogame.utilities.datastorage.subtables.PlayerTable;
import ogame.utilities.defenses.DefenseType;
import ogame.utilities.defenses.Defenses;
import ogame.utilities.planets.Planet;
import ogame.utilities.research.Research;
import ogame.utilities.research.ResearchType;
import ogame.utilities.ships.ShipType;
import ogame.utilities.ships.Ships;

/**
 * This object is responsible for 
 * @author scchang
 */
public class OgameDataStorage {
    ///////////////////////////////////////////////////////////////////////////
    /**
     * The name of the current database.
     */
    protected String database_name;
    
    /**
     * The connection for the current database.
     */
    protected SQLiteConnection db;
    
    /**
     * The object to handle interactions with the player table.
     */
    protected PlayerTable playerTable;
    
    /**
     * The object to handle interaction with the fleet table.
     */
//    protected FleetTable fleetTable;
    
    /**
     * The object to handle interaction with the defense table.
     */
//    protected DefenseTable defenseTable;
    
    /**
     * The object to handle interaction with the planet table.
     */
    protected PlanetTable planetTable;
    
    /**
     * The object to handle interaction with the general table.
     */
    protected GeneralTable generalTable;
    
    ///////////////////////////////////////////////////////////////////////////
    /**
     * Initialize database with the given name.
     * @param database_name, The name of the database
     */
    
    public OgameDataStorage(String database_name){
        this.database_name = database_name;
        this.initializeDatabase();
    }
    
    ///////////////////////////////////////////////////////////////////////////
    /**
     * Initialize database with default name.
     */
    public OgameDataStorage(){
        this(Constants.DATA_STORAGE_FILENAME);
    }
    
    ////////////////////////////////////////////////////////////////////////////
    /**
     * Instantiate the database that this class interacts with.
     * @throws SQLiteException 
     */
    protected void initializeDatabase(){
        this.db = new SQLiteConnection(new File(this.database_name));
        
        try{
            this.db.open(true);
            
            this.playerTable = new PlayerTable(this.db, this);
//            this.fleetTable = new FleetTable(this.db);
//            this.defenseTable = new DefenseTable(this.db);
            this.generalTable = new GeneralTable(this.db);
            this.planetTable = new PlanetTable(this.db, this);


        }catch(SQLiteException e){
            System.out.println("Unable to initialize database tables.");
            e.printStackTrace();
        }

    }

    /**
     * @return the playerTable
     */
    public PlayerTable getPlayerTable() {
        return playerTable;
    }

    /**
     * @return the planetTable
     */
    public PlanetTable getPlanetTable() {
        return planetTable;
    }

    /**
     * @return the generalTable
     */
    public GeneralTable getGeneralTable() {
        return generalTable;
    }
    
     public static boolean unitTest(){
        
        Player player = new Player("Tester");
        Planet planetOne = new Planet(player, "one", new Coords(1, 155, 10));
        Planet planetTwo = new Planet(player, "two", new Coords(2, 255, 1));
        Planet planetThree = new Planet(player, "three", new Coords(3, 145, 7));
        Planet planetFour = new Planet(player, "four", new Coords(4, 200, 4));
        Planet planetFive = new Planet(player, "five", new Coords(5, 68, 5));
        
        Player playerTwo = new Player("Oracle");
        Planet onePlanet = new Planet(playerTwo, "six", new Coords(1, 2, 3));
        Planet twoPlanet = new Planet(playerTwo, "seven", new Coords(3, 2, 1));
        Planet threePlanet = new Planet(playerTwo, "eight", new Coords(5, 3, 2));
        
        Planet[] initialPlanets = {planetOne, planetTwo, planetThree, planetFour,
            planetFive, onePlanet, twoPlanet, threePlanet};
        
        Player[] initialPlayers = {player, playerTwo};
        
        Research researchOne = new Research();
        Research researchTwo = new Research();
        int i = 0;
        for(ResearchType type : ResearchType.values()){
            researchOne.put(type, i);
            i++;
        }
        for(ResearchType type : ResearchType.values()){
            researchTwo.put(type, i);
            i--;
        }
        Research[] research = {researchOne, researchTwo};
        player.setResearch(researchOne);
        playerTwo.setResearch(researchTwo);
        
        i = 0;
        Ships[] ships = new Ships[8];
        for(int j = 0; j < ships.length; ++j){
            ships[j] = new Ships();
        }
        for(ShipType type : ShipType.values()){
            for(Ships fleet : ships){
                fleet.put(type, i);
                i++;
            }
        }
        
        i = 0;
        Defenses[] defenses = new Defenses[8];
        for(int j = 0; j < defenses.length; ++j){
            defenses[j] = new Defenses();
        }
        for(DefenseType type : DefenseType.values()){
            for(Defenses defense : defenses){
                defense.put(type, i);
                i++;
            }
        }
        
        i = 0;
        Resources[] resources = new Resources[8];
        for(int j = 0; j < resources.length; ++j){
            resources[j] = new Resources();
            resources[j].setMetal(i);
            i += 1000;
            resources[j].setCrystal(i);
            i += 1000;
            resources[j].setDeut(i);
            i += 1000;
        }
        
        i = 0;
        Buildings[] buildings = new Buildings[8];
        for(int j = 0; j < buildings.length; ++j){
            buildings[j] = new Buildings();
            for(BuildingType type : BuildingType.values()){
                buildings[j].put(type, i);
                i++;
                if(i == 100){
                    i = 0;
                }
            }
            initialPlanets[j].setBuildings(buildings[j]);
        }
        
        
        planetOne.setShips(ships[0]);
        planetTwo.setShips(ships[1]);
        planetThree.setShips(ships[2]);
        planetFour.setShips(ships[3]);
        planetFive.setShips(ships[4]);
        
        onePlanet.setShips(ships[5]);
        twoPlanet.setShips(ships[6]);
        threePlanet.setShips(ships[7]);
        
        planetOne.setDefenses(defenses[0]);
        planetTwo.setDefenses(defenses[1]);
        planetThree.setDefenses(defenses[2]);
        planetFour.setDefenses(defenses[3]);
        planetFive.setDefenses(defenses[4]);
        
        onePlanet.setDefenses(defenses[5]);
        twoPlanet.setDefenses(defenses[6]);
        threePlanet.setDefenses(defenses[7]);
        
        planetOne.setResources(resources[0]);
        planetTwo.setResources(resources[1]);
        planetThree.setResources(resources[2]);
        planetFour.setResources(resources[3]);
        planetFive.setResources(resources[4]);
        
        onePlanet.setResources(resources[5]);
        twoPlanet.setResources(resources[6]);
        threePlanet.setResources(resources[7]);
        
        player.addPlanet(planetOne);
        player.addPlanet(planetTwo);
        player.addPlanet(planetThree);
        player.addPlanet(planetFour);
        player.addPlanet(planetFive);
        
        playerTwo.addPlanet(onePlanet);
        playerTwo.addPlanet(twoPlanet);
        playerTwo.addPlanet(threePlanet);
        
        OgameDataStorage ogameDataStorage = new OgameDataStorage("Once");
        
        ogameDataStorage.getPlanetTable().modifyTable(planetOne);
        ogameDataStorage.getPlanetTable().modifyTable(planetTwo);
        ogameDataStorage.getPlanetTable().modifyTable(planetThree);
        ogameDataStorage.getPlanetTable().modifyTable(planetFour);
        ogameDataStorage.getPlanetTable().modifyTable(planetFive);
        
        ogameDataStorage.getPlanetTable().modifyTable(onePlanet);
        ogameDataStorage.getPlanetTable().modifyTable(twoPlanet);
        ogameDataStorage.getPlanetTable().modifyTable(threePlanet);
        
        int planet_id[] = new int[8];
        planet_id[0] = ogameDataStorage.getPlanetTable().getPlanetID(planetOne);
        planet_id[1] = ogameDataStorage.getPlanetTable().getPlanetID(planetTwo);
        planet_id[2] = ogameDataStorage.getPlanetTable().getPlanetID(planetThree);
        planet_id[3] = ogameDataStorage.getPlanetTable().getPlanetID(planetFour);
        planet_id[4] = ogameDataStorage.getPlanetTable().getPlanetID(planetFive);
        
        planet_id[5] = ogameDataStorage.getPlanetTable().getPlanetID(onePlanet);
        planet_id[6] = ogameDataStorage.getPlanetTable().getPlanetID(twoPlanet);
        planet_id[7] = ogameDataStorage.getPlanetTable().getPlanetID(threePlanet);
        
        for(int j = 0; j < planet_id.length; ++j){
            if(planet_id[j] != j+1){
                System.out.println("Mismatching planet ids: got: " + planet_id +
                        " expected " + j+1);
                return false;
            }
        }
        
        Planet planets[] = new Planet[8];
        for(int j = 0; j < planets.length; ++j){
            planets[j] = ogameDataStorage.getPlanetTable().getPlanetFromID(planet_id[j]);
            if(!planets[j].getName().equals(initialPlanets[j].getName())){
                System.out.println("Planet name mismatch got: " + planets[j].getName() +
                        " expected: " + initialPlanets[j].getName());
                return false;
            }
            if(!planets[j].getCoords().equals(initialPlanets[j].getCoords())){
                System.out.format("Coord mismatch got: %s expected: %s\n",
                        planets[j].getCoords().toString(),
                        initialPlanets[j].getCoords().toString());
                return false;
            }
        }
        
        Resources[] resultResources = new Resources[8];
        for(int j = 0; j < resultResources.length; ++j){
            resultResources[j] = planets[j].getResources();
            if(resultResources[j].getMetal() != resources[j].getMetal()){
                System.out.format(("Metal Resource mismatch, got: %d expected: %d\n"), 
                        resultResources[j].getMetal(), resources[j].getMetal());
                return false;
            }else if(resultResources[j].getCrystal() != resources[j].getCrystal()){
                System.out.format(("Crystal Resource mismatch, got: %d expected: %d\n"), 
                        resultResources[j].getCrystal(), resources[j].getCrystal());
                return false;
            }else if(resultResources[j].getDeut() != resources[j].getDeut()){
                System.out.format(("Deut Resource mismatch, got: %d expected: %d\n"), 
                        resultResources[j].getDeut(), resources[j].getDeut());
                return false;
            }
        }
        
        Ships[] resultShips = new Ships[8];
        for(int j = 0; j < resultResources.length; ++j){
            resultShips[j] = planets[j].getShips();
            if(resultShips[j].keySet().size() !=
                    ships[j].keySet().size()){
                System.out.format("Ship key size mismatch got: %d expected %d\n",
                        resultShips[j].keySet().size(),
                        ships[j].keySet().size());
                return false;
            }
            for(ShipType type : resultShips[j].keySet()){
                if(resultShips[j].get(type) != ships[j].get(type)){
                    System.out.format("Ship type : " + type + " got number: %d expected %d \n",
                            resultShips[j].get(type),
                            ships[j].get(type));
                    return false;
                }
            }
        }
        
        Defenses[] resultDefenses = new Defenses[8];
        for(int j = 0; j < resultDefenses.length; ++j){
            resultDefenses[j] = planets[j].getDefenses();
            if(resultDefenses[j].keySet().size() !=
                    defenses[j].keySet().size()){
                System.out.format("Defense key size mismatch got : %d expected %d\n",
                        resultDefenses[j].keySet().size(),
                        defenses[j].keySet().size());
                return false;
            }
            for(DefenseType type : resultDefenses[j].keySet()){
                if(resultDefenses[j].get(type) != defenses[j].get(type)){
                    System.out.format("Defense type : " + type + " got number: %d expected %d\n",
                            resultDefenses[j].get(type),
                            defenses[j].get(type));
                    return false;
                }
            }
        }
        
        Buildings[] resultBuildings = new Buildings[resultDefenses.length];
        for(int j = 0; j < resultBuildings.length; ++j){
            resultBuildings[j] = planets[j].getBuildings();
            for(BuildingType type : BuildingType.values()){
                if(resultBuildings[j].get(type) != buildings[j].get(type)){
                    System.out.format("Building num with " + type + ", got: %d expected %d\n",
                            resultBuildings[j].get(type),
                            buildings[j].get(type));
                    return false;
                }
            }
        }
        
        int playerIds[] = new int[2];
        playerIds[0] = ogameDataStorage.getPlayerTable().getPlayerID(player);
        playerIds[1] = ogameDataStorage.getPlayerTable().getPlayerID(playerTwo);
        
        for(int j = 0; j < playerIds.length; ++j){
            if(playerIds[j] != j+1){
                System.out.format("Player id mismatch, got: %d expected: %d\n", 
                        playerIds[j], j+1);
                return false;
            }
        }
        
        Player[] players = new Player[playerIds.length];
        for(int j = 0; j < playerIds.length; ++j){
            players[j] = ogameDataStorage.getPlayerTable().getPlayerByID(playerIds[j]);
            if(!players[j].getName().equals(initialPlayers[j].getName())){
                System.out.println("Player name got: " + players[j].getName() 
                        + " expected: " + initialPlayers[j].getName());
                return false;
            }
        }
        
        Research[] researchResults = new Research[players.length];
        for(int j = 0; j < researchResults.length; ++j){
            researchResults[j] = players[j].getResearch();
            for(ResearchType type : ResearchType.values()){
                if(researchResults[j].get(type) != research[j].get(type)){
                    System.out.format("Research mismatch" + type + ", got: %d expected: %d\n",
                            researchResults[j].get(type),
                            research[j].get(type));
                    return false;
                }
            }
        }
        
        return true;
    }
    
}
